// ChessFinal.cpp : Defines the entry point for the application.
//

#include <process.h>
#include "stdafx.h"
#include "ChessFinal.h";
#include "Socket.h";
#include "Game.h";
#include <windows.h>

#pragma comment (lib, "Msimg32.lib")

#define MAX_LOADSTRING 100
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 650
// Global Variables:
HINSTANCE hInst;								// current instance
HWND hWnd;
HDC hdc;
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	Conn(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	CreateGame(HWND, UINT, WPARAM, LPARAM);

void DrawBoard();
void DrawPiece(PieceType tp, int x, int y, int rect);
void DrawImage(UINT id, int x, int y, int rect);
void DrawImageA(UINT id, int x, int y);
void HandleMouseClick(int x, int y);
void DrawBG();
void Thread(void* pParams);

Sock * so; // Socket for net communication
Game * game;
char* oppmove;

int selectx;
int selecty;

LPWSTR testStr;

const int leftMargin = 15;
const int topMargin = 15;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;
	game = NULL;
	testStr = NULL;
	selectx = 0;
	selecty = 0;
	oppmove = new char[4];

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_CHESSFINAL, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CHESSFINAL));

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CHESSFINAL));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_CHESSFINAL);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	
	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	int posx, posy;

	PAINTSTRUCT ps;
		
	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			delete so;
			DestroyWindow(hWnd);
			break;
		case ID_GAME_SAVE:
			game->SaveGame();
		case ID_NEWGAME_PLAYASWHITE:
			game = new Game(Color::WHITE);
			DialogBox(hInst, MAKEINTRESOURCE(IDD_CREATE), hWnd, CreateGame);
			game->StartGame();
			_beginthread(Thread, 0, NULL);
			InvalidateRect(hWnd,NULL,true);
			break;
		case ID_NEWGAME_PLAYASBLACK:
			game = new Game(Color::BLACK);
			DialogBox(hInst, MAKEINTRESOURCE(IDD_CREATE), hWnd, CreateGame);
			game->StartGame();
			_beginthread(Thread, 0, NULL);
			InvalidateRect(hWnd,NULL,true);
			break;
		case ID_GAME_CONNECT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_CONNECT),hWnd,Conn);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_GETMINMAXINFO:
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		//TextOut(hdc,50,50,L"Connect..", 9);
		DrawBoard();
		//if (testStr != NULL) TextOut(hdc, 600, 500, testStr, sizeof(testStr));
		// TODO: Add any drawing code here...
		EndPaint(hWnd, &ps);
		break;
	case WM_LBUTTONDOWN:
		posx = LOWORD(lParam);
		posy = HIWORD(lParam);
		HandleMouseClick(posx, posy);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

// Message handler for Connect Dialog.
INT_PTR CALLBACK Conn(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);

	HWND h1, h2;
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
			int port, len;
			char * ip;
			char * s;
			case IDOK:
				h1 = GetDlgItem(hDlg, IDC_IPADDRESS1);
				h2 = GetDlgItem(hDlg, IDC_PORT_NUMBER);
				
				LPWSTR ipAddr, portS;
				//GetDlgItemText(hDlg, IDC_IPADDRESS1, testStr, 255);
				
				ipAddr = (LPWSTR) GlobalAlloc(GPTR, 255);
				GetWindowText(h1, ipAddr, 254);
				
				ip = new char[wcslen(ipAddr)];
				//it = ipAddr;
				for (int i=0; i<wcslen(ipAddr); i++)
				{
					
				}
				portS = (LPWSTR)GlobalAlloc(GPTR, 255);
				GetWindowText(h2,portS, 254);
				port = strlen(ip);
				port = 0;
				for (int i=0; i<wcslen(portS); i++)
				{
					port = 10 * port + (portS[i]-'0');
				}
				
				//TextOut(hdc, 600, 10, lpStr, 10);
				EndDialog(hDlg, LOWORD(wParam));
				so = new ClientSocket("127.0.0.1", port); // Connect to the server
				if (so->ReceiveData()[0] == '1')
				{
					game = new Game(Color::BLACK);
				}
				else
				{
					game = new Game(Color::WHITE);
				}

				so->SendData("0");
				//game = new Game()
				
				//len = (int) (so->ReceiveData()[0]);
				//so->SendData("0");
				////s = new char[len];
				//s = so->ReceiveData();
				//game->SetGame(s);
				//game->StartGame();
				//so->SendData("0");
				game->StartGame();
				_beginthread(Thread, 0, NULL);
				return (INT_PTR) TRUE;
			case IDCANCEL:
				EndDialog(hDlg, LOWORD(wParam));
				return (INT_PTR)FALSE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

INT_PTR CALLBACK CreateGame(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);

	HWND h1, h2;
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
			int port;
			char* c;
			
			case IDOK:
				h2 = GetDlgItem(hDlg, IDC_PORT_NUMBER1);
				
				LPWSTR portS;
				
				portS = (LPWSTR)GlobalAlloc(GPTR, 255);
				GetWindowText(h2,portS, 254);
				
				port = 0;
				//port = wcslen(portS);
				for (int i=0; i<wcslen(portS); i++)
				{
					port = 10 * port + (portS[i]-'0');
				}
				
				//TextOut(hdc, 600, 10, lpStr, 10);
				EndDialog(hDlg, LOWORD(wParam));
				so = new ServerSocket(port); 
				game->StartGame();
				
				if (game->myCol == Color::WHITE) so->SendData("1");
				else so->SendData("0");
				so->ReceiveData();

				//c = new char[1];
				//c[0] = (char)strlen(game->InitFEN);
				//so->SendData(c);
				//so->ReceiveData();
				//so->SendData(game->InitFEN);
				//so->ReceiveData();

				return (INT_PTR) TRUE;
			case IDCANCEL:
				EndDialog(hDlg, LOWORD(wParam));
				return (INT_PTR)FALSE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}
int getRect(const Piece* p)
{
	if (p->col == Color::BLACK)
	{
		return 0;
	}
	return 1;
}

void DrawFrame()
{
	if (game == NULL) return;
	if (selectx != 0)
	{
		int posx = leftMargin + 70 * (selectx - 1);
		int posy = topMargin + 70 * (selecty - 1);

		HDC hdcMem;
		HGDIOBJ oldbm ;
		BITMAP bmp;
		HBITMAP hbm = LoadBitmap(hInst, MAKEINTRESOURCE(ID_FRAME));
		hdcMem = CreateCompatibleDC(hdc);
		oldbm = SelectObject(hdcMem, hbm);
		GetObject(hbm, sizeof(bmp),&bmp);
		BitBlt(hdc, posx, posy, 70, 70, hdcMem, 0, 0, SRCAND);
		SelectObject(hdcMem, oldbm);
		DeleteDC(hdcMem);
	}
}

void DrawPieces()
{
	if (game == NULL) return;

	for (int i=1; i<9;i++)
		for (int j=1; j<9; j++)
		{
			if (game->board[i][j] != NULL)
			{
				int r = getRect(game->board[i][j]);
				if ((i+j)%2 == 1) r += 2;
				int posx = 70 * (j - 1);
				int posy = 70 * (i - 1);
				if (game->myCol == Color::BLACK)
				{
					posx = 490 - posx;
					posy = 490 - posy;
				}

				posx += leftMargin;
				posy += topMargin;
				DrawPiece(game->board[i][j]->tp, posx, posy, r);
			}
		}
}

void DrawCaptured()
{
	if (game == NULL) return;
	std::vector<Piece>::iterator it;

	std::vector<Piece> q = game->capturedW;
	int i = 0;
	for (it = q.begin(); it != q.end(); ++it)
	{
		DrawPiece(it->tp, 8*70+2*leftMargin + 85, topMargin + i * 70, 1);
	}

	q = game->capturedB;
	i = 0;
	for (it = q.begin(); it != q.end(); ++it)
	{
		DrawPiece(it->tp, 8*70+2*leftMargin + 85, topMargin + i * 70, 0);
	}
}

void DrawBoard()
{
	DrawBG();
	DrawCaptured();
	DrawPieces();
	DrawFrame();
}

void DrawPiece(PieceType tp, int x, int y, int rect)
{
	
	if (game == NULL) return;
	//DrawImage(ID_WPAWN,0,0);
	UINT id = 0;
	switch(tp)
	{
	case PieceType::TP_BISHOP:
		id = ID_BISHOP;
		break;
	case PieceType::TP_KING:
		id = ID_KING;
		break;
	case PieceType::TP_KNIGHT:
		id = ID_KNIGHT;
		break;
	case PieceType::TP_PAWN:
		id = ID_PAWN;
		break;
	case PieceType::TP_QUEEN:
		id = ID_QUEEN;
		break;
	case PieceType::TP_ROOK:
		id = ID_ROOK;
		break;
	}

	if (id == 0) return;

	DrawImage(id, x, y, rect);
	//DrawImage(ID_BISHOP,85,15);
}

void DrawBG()
{
	int topMargin = 15;
	int leftMargin = 15;

	for (int i=0; i < 8; i++)
	{
		for (int j = 0; j < 8; j++)
		{
			if ((i+j)%2 == 0) DrawImage(ID_LIGHT, leftMargin + 70 * i, topMargin + 70 * j, 0);
			else DrawImage(ID_DARK, leftMargin + 70 * i, topMargin + 70 * j, 0);
		}
	}
}

void DrawImage(UINT id,int x, int y, int r)
{
	HDC hdcMem;
	HGDIOBJ oldbm ;
	BITMAP bmp;
	HBITMAP hbm = LoadBitmap(hInst, MAKEINTRESOURCE(id));
	hdcMem = CreateCompatibleDC(hdc);
	oldbm = SelectObject(hdcMem, hbm);
	GetObject(hbm, sizeof(bmp),&bmp);
	BitBlt(hdc, x, y, 70, 70, hdcMem, r*70, 0, SRCCOPY);
	SelectObject(hdcMem, oldbm);
	DeleteDC(hdcMem);
}

void DrawImageA(UINT id,int x, int y)
{
	HDC hdcMem;
	HGDIOBJ oldbm ;
	BITMAP img;
	HBITMAP him = LoadBitmap(hInst, MAKEINTRESOURCE(id));
	hdcMem = CreateCompatibleDC(hdc);
	oldbm = SelectObject(hdcMem, him);
	GetObject(him, sizeof(img),&img);
	BLENDFUNCTION bf;
	bf.BlendFlags = 0;
	bf.AlphaFormat = 0;
	bf.SourceConstantAlpha = 30;
	bf.BlendOp = AC_SRC_OVER;

	AlphaBlend(hdc, x, y, img.bmWidth, img.bmHeight, hdcMem, 0,0,img.bmWidth, img.bmHeight, bf);
	//BitBlt(hdc, x, y, img.bmWidth, img.bmHeight, hdcMem,0,0,SRCCOPY);
	SelectObject(hdcMem, oldbm);
	DeleteDC(hdcMem);
}

void HandleMouseClick(int x, int y)
{
	if (game == NULL || !(game->myTurn)) return;

	if (x > leftMargin && x < 560 + leftMargin && y > topMargin && y < 560 + topMargin) //is in board
	{
		x = ((x - leftMargin) / 70) + 1;
		y = ((y - topMargin) / 70) + 1;
		
		if (selectx == 0)
		{
			int fx = x;
			int fy = y;

			if (game->myCol == Color::BLACK)
			{
				fx = 9 - x;
				fy = 9 - y;
			}
			if (game->board[fy][fx] == NULL || game->board[fy][fx]->col != game->myCol) return;
			selectx = x;
			selecty = y;
		}
		else
		{
			if (x != selectx || y != selecty)
			{
				char* mv = new char[4];
				int fx = selectx;
				int fy = selecty;
				int tx = x;
				int ty = y;

				if (game->myCol == Color::BLACK)
				{
					fx = 9 - selectx;
					fy = 9 - selecty;
					tx = 9 - x;
					ty = 9 - y;
				}

				mv[0] = (char) (fy + 'a' - 1);
				mv[1] = (char) (fx + '0');
				mv[2] = (char) (ty + 'a' - 1);
				mv[3] = (char) (tx + '0');
				int iRes = game->Move(mv);
				if (iRes == 0) 
				{
					if (game->myCol == Color::BLACK) game->myCol = Color::WHITE;
					else game->myCol = Color::BLACK;

					//game->myTurn = false;				
					//so->SendData(mv);
					//so->ReceiveData(255);
				}
				if (iRes == 999)
				{
					MessageBox(hWnd, L"Checkmate", L"Checkmate", MB_OK);
					delete game;
					game = NULL;
				}
			}
			selectx = 0;
			selecty = 0;
		}
	}
	DrawCaptured();
	InvalidateRect(hWnd,NULL,true);
}

void Game::SetGame(char *FEN)
{
	InitFEN = &(*FEN);
}

void Thread(void* pParams)
{
	while (1)
	{
		if (game == NULL) continue;
		if (!game->myTurn)
		{
			oppmove = so->ReceiveData(5);
			so->ReceiveData();
			game->Move(oppmove);
			oppmove = new char[5];
			game->myTurn = true;
		}
	}
}
